package pers.ree.demo.concurrent;

import java.util.concurrent.*;

public class ThreadPoolExecutorDemo {

//    private static ExecutorService cachedThreadPool = new ThreadPoolExecutor(4, Runtime.getRuntime().availableProcessors() * 2, 0, TimeUnit.MILLISECONDS, new SynchronousQueue<>(), r -> new Thread(r, "ThreadTest"));


    public static void main(String[] args) {
        // LinkedBlockingQueue没有指定容量的情况下为无界队列，
        // 测试到queue size: 3233216的时候，pool size仍然是1
//        testLinkedBlockingQueueUpperLimit();

        // 验证线程池创建线程及保存线程的顺序
//        testThreadSaveSequence();

        // 验证使用SynchronousQueue的线程池
        testSynchronousQueue();
    }

    /**
     * 验证使用LinkedBlockingQueue作为等待队列的时候，
     * 是否存在实际的上限：
     * 即queue size达到某个比较大的值时线程池是否会增加池中的线程数到maximumPoolSize，
     * 然后在继续添加任务时触发AbortPolicy策略抛出异常。
     *
     * 实际测试结果：
     * 测试到queue size: 3233216的时候，pool size仍然是1，手动中断了测试。
     *
     * 结论：
     * 使用LinkedBlockingQueue作为等待队列的时候，maximumPoolSize如果大于corePoolSize是无意义的，
     * 拒绝策略也没有实际作用
     */
    public static void testLinkedBlockingQueueUpperLimit() {
        // LinkedBlockingQueue不带参数时是无界队列
        ExecutorService executorService = new ThreadPoolExecutor(1, 2,
                0L, TimeUnit.MILLISECONDS,
                new LinkedBlockingQueue<>(),
                Executors.defaultThreadFactory(),
                new ThreadPoolExecutor.AbortPolicy()); // AbortPolicy：抛出异常

        while(true) {
            execute(executorService);
            printExecutorServiceInfo(executorService);
        }
    }

    /**
     * 该方法逻辑与testLinkedBlockingQueueUpperLimit基本一致，
     * 但是给LinkedBlockingQueue加了参数，
     * 目的是让其作为有界队列验证ThreadPoolExecutor中用于保存新增的线程的几个数据结构的保存顺序：
     * 1.先新增核心线程
     * 2.达到核心线程上限后，添加到队列中
     * 3.达到队列上限但是未达到最大线程数时，新增非核心线程
     * 4.达到最大线程数上限后，触发拒绝策略
     * */
    public static void testThreadSaveSequence() {
        // LinkedBlockingQueue不带参数时是无界队列
        ExecutorService executorService = new ThreadPoolExecutor(2, 4,
                0L, TimeUnit.MILLISECONDS,
                new LinkedBlockingQueue<>(3),
                Executors.defaultThreadFactory(),
                new ThreadPoolExecutor.CallerRunsPolicy()); // CallerRunsPolicy在主线程执行任务

        while(true) {
            execute(executorService);
            printExecutorServiceInfo(executorService);
        }
    }

    /**
     * 验证使用SynchronousQueue作为等待队列的线程池
     * 该示例与newCachedThreadPool的底层逻辑近似（为了测试方便只是将最大线程数设置为3）
     * 预期效果：
     * 1.线程数增长到3后，再次新增任务时抛出异常，主线程停止执行，只有3个子线程继续执行
     * 2.由于SynchronousQueue是阻塞的，3个子线程在第1次执行print时就会阻塞
     */
    public static void testSynchronousQueue() {
        ExecutorService executorService = new ThreadPoolExecutor(0, 3,
                60L, TimeUnit.SECONDS,
                new SynchronousQueue<>(),
                Executors.defaultThreadFactory(),
                new ThreadPoolExecutor.AbortPolicy());

        while(true) {
            // 如果使用了try catch，会循环抛出异常
//            try {
//                execute(executorService);
//            } catch (Exception e) {
//                e.printStackTrace();
//            }
            execute(executorService);
            printExecutorServiceInfo(executorService);
        }
    }

    static void execute(ExecutorService executorService) {
        System.out.println(Thread.currentThread().getName() + " running in execute begin...");
        executorService.execute(() -> print());
        System.out.println(Thread.currentThread().getName() + " running in execute end...");

//        System.out.println("queue size: " + ((ThreadPoolExecutor) executorService).getQueue().size());
//        System.out.println("pool size: " + ((ThreadPoolExecutor) executorService).getPoolSize());
//        System.out.println("active count: " + ((ThreadPoolExecutor) executorService).getActiveCount());
    }

    public static void printExecutorServiceInfoWithoutSleepTime(ExecutorService executorService) {
        System.out.println("queue size: " + ((ThreadPoolExecutor) executorService).getQueue().size());
        System.out.println("pool size: " + ((ThreadPoolExecutor) executorService).getPoolSize());
        System.out.println("active count: " + ((ThreadPoolExecutor) executorService).getActiveCount());
        System.out.println("task count: " + ((ThreadPoolExecutor) executorService).getTaskCount());
        System.out.println("completed task count: " + ((ThreadPoolExecutor) executorService).getCompletedTaskCount());
    }

    public static void printExecutorServiceInfo(ExecutorService executorService) {
        printExecutorServiceInfoWithoutSleepTime(executorService);

        try {
            TimeUnit.MILLISECONDS.sleep(1000L);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    private static void print() {
        int i = 0;
        while (true) {
            System.out.println(Thread.currentThread().getName() + " running in print, i=" + i++);
            try {
                TimeUnit.MILLISECONDS.sleep(100000L);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

//        int i = 10;
//        while (i > 0) {
//            System.out.println(Thread.currentThread().getName() + " running in print, i="+i);
//            try {
////                if (!Thread.currentThread().getName().equals("main")) {
////                    TimeUnit.MILLISECONDS.sleep(1000L);
////                }
//                TimeUnit.MILLISECONDS.sleep(1000L);
//            } catch (InterruptedException e) {
//                e.printStackTrace();
//            }
//            i--;
//        }
    }
}
